package password.bee.liciense;



import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
import org.apache.log4j.Logger;
import password.bee.liciense.Switch;

public class License {
   static final Logger Log = Logger.getLogger(License.class);
    private static String TAG = License.class.getName();
    private static boolean dbg = true;

    private String newline = System.getProperty("line.separator");

    private String companyInfoStartMark = "======start company info======";
    private String companyInfoEndMark = "======end company info======";
    private String SNsStartMark = "======start SN======";
    private String SNsEndMark = "======end SN======";
    private String switchsStartMark = "======start switch======";
    private String switchsEndMark = "======end switch======";
    private String appStartMark = "======start app======";
    private String appEndMark = "======end app======";
    private String signDateStartMark = "======start sign date======";
    private String signDateEndMark = "======end sign date======";
    private String signatureStartMark = "======start signature======";
    private String signatureEndMark = "======end signature======";
    private String magicString = "beeboxes2022";
    private String propertyPrefix = "persist.license.";

    private StringBuffer rawMagic = new StringBuffer("");
    private StringBuffer rawCompanyInfo = new StringBuffer("");
    private StringBuffer rawSNs = new StringBuffer("");
    private StringBuffer rawSwitches = new StringBuffer("");
    private StringBuffer rawApps = new StringBuffer("");
    private StringBuffer rawSignDate = new StringBuffer("");
    private StringBuffer rawSignature = new StringBuffer("");
    private StringBuffer rawCertificate = new StringBuffer("");
    private StringBuffer rawLicenseData = new StringBuffer("");

    private HashMap<String, String> mapCompanyInfo = new HashMap<String, String>();
    private ArrayList<String> listSNs = new ArrayList<String>();
    private HashMap<String, Switch> mapSwitches = new HashMap<String, Switch>();
    private HashMap<String, String> mapApps = new HashMap<String, String>();

    private boolean parsed = false;

    public synchronized boolean parse(String filename) {
        File lf = new File(filename);
        if (!lf.exists()) {
            return false;
        }

        if (breakDown(filename) == false) {
            return false;
        }

        if (parseCompanyInfo() == false) {
            return false;
        }

        if (parseSNs() == false) {
            return false;
        }

        if (parseSwitches() == false) {
            return false;
        }

        if (parseApps() == false) {
            return false;
        }

        if (checkMagic() == false) {
            return false;
        }

//        todo
//        if (checkSN() == false) {
//            return false;
//        }

        //todo  取消证书校验 .dat 文件的pub证书
//        if (checkCertificate() == false) {
//            return false;
//        }

        // 签名校验
        if (checkSignature() == false) {
            return false;
        }

        parsed = true;
        return true;
    }

    public synchronized Date getSignDate() {
        if (parsed) {
            try {
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy.MM.dd");
                Date date = sdf.parse(rawSignDate.toString());
                return date;
            } catch (Exception e) {
                if (dbg) {
                    Log.error(TAG+ "" + e.getMessage());
                    e.printStackTrace();
                }

                return null;
            }
        } else {
            return null;
        }
    }

    public synchronized ArrayList<Switch> getSwitches() {
        if (parsed) {
            ArrayList<Switch> ret = new ArrayList<Switch>();
            for (HashMap.Entry<String, Switch> entry : mapSwitches.entrySet()) {
                Switch value = entry.getValue();
                ret.add(value);
            }
            return ret;
        } else {
            return null;
        }
    }

    public synchronized Map<String, String> getApps() {
        if (parsed) {
            return mapApps;
        } else {
            return null;
        }
    }

    private boolean getRawMagic(BufferedReader brFile) {
        try {
            String tempString = brFile.readLine();
            if (tempString == null) {
                if (dbg) {
                    Log.error(TAG+ "no magic line found");
                }
                return false;
            }
            rawMagic.append(tempString);
            rawLicenseData.append(rawMagic);
        } catch (Exception e) {
            if (dbg) {
                Log.error(TAG+ "" + e.getMessage());
                e.printStackTrace();
            }
        }

        return true;
    }

    private boolean getRawInfo(BufferedReader brFile, StringBuffer targetInfo,
                               String startMark, String endMark,
                               String message) {
        try {
            String tempLine = brFile.readLine();
            boolean foundEnd = false;
            if (tempLine != null && tempLine.equals(startMark)) {
                if (!message.equals("signature")) {
                    rawLicenseData.append(newline).append(tempLine);
                }
                while ((tempLine = brFile.readLine()) != null) {
                    if (!message.equals("signature")) {
                        rawLicenseData.append(newline).append(tempLine);
                    }
                    if (tempLine.equals(endMark)) {
                        foundEnd = true;
                        break;
                    }
                    if (targetInfo.toString().equals("")) {
                        targetInfo.append(tempLine);
                    } else {
                        targetInfo.append(newline).append(tempLine);
                    }
                }
            } else {
                if (dbg) {
                    Log.error(TAG+ "no " + message + " segment start mark found");
                }

                return false;
            }
            if (foundEnd == false) {
                if (dbg) {
                    Log.error(TAG+ "no " + message + " segment end mark found");
                }
                return false;
            }
        } catch (Exception e) {
            if (dbg) {
                Log.error(TAG+ "" + e.getMessage());
                e.printStackTrace();
            }

            return false;
        }

        return true;
    }

    private boolean getRawCertificate(BufferedReader brFile) {
        try {
            String tempString = brFile.readLine();
            if (tempString == null) {
                if (dbg) {
                    Log.error(TAG+ "no certificate line found");
                }
                return false;
            }
            rawCertificate.append(tempString);
        } catch (Exception e) {
            if (dbg) {
                Log.error(TAG+ "" + e.getMessage());
                e.printStackTrace();
            }
        }

        return true;
    }

    private boolean breakDown(String filename) {
        try (BufferedReader brFile =
                     new BufferedReader(new FileReader(filename))
        ) {
            if (getRawMagic(brFile) == false) {

                return false;
            }

            if (getRawInfo(brFile, rawCompanyInfo,
                    companyInfoStartMark,
                    companyInfoEndMark, "company info") == false) {

                return false;
            }

            if (getRawInfo(brFile, rawSNs, SNsStartMark, SNsEndMark, "SN") == false) {

                return false;
            }

            if (getRawInfo(brFile, rawSwitches, switchsStartMark,
                    switchsEndMark, "switch") == false) {

                return false;
            }

            if (getRawInfo(brFile, rawApps, appStartMark,
                    appEndMark, "app section") == false) {

                return false;
            }

            if (getRawInfo(brFile, rawSignDate, signDateStartMark,
                    signDateEndMark, "sign date") == false) {

                return false;
            }

            if (getRawInfo(brFile, rawSignature, signatureStartMark,
                    signatureEndMark, "signature") == false) {

                return false;
            }

            if (getRawCertificate(brFile) == false) {

                return false;
            }

            if (dbg) {
                Log.error(TAG+ "====raw license section====");
                Log.error(TAG+ rawMagic.toString());
                Log.error(TAG+ "=========================");
                Log.error(TAG+ rawCompanyInfo.toString());
                Log.error(TAG+ "=========================");
                Log.error(TAG+ rawSNs.toString());
                Log.error(TAG+ "=========================");
                Log.error(TAG+ rawSwitches.toString());
                Log.error(TAG+ "=========================");
                Log.error(TAG+ rawApps.toString());
                Log.error(TAG+ "=========================");
                Log.error(TAG+ rawSignDate.toString());
                Log.error(TAG+ "=========================");
                Log.error(TAG+ rawSignature.toString());
                Log.error(TAG+ "=========================");
                Log.error(TAG+ rawCertificate.toString());
                Log.error(TAG+ "=========================");
                Log.error(TAG+ rawLicenseData.toString());
                Log.error(TAG+ "==========================");
            }

            return true;
        } catch (Exception e) {
            if (dbg) {
                Log.error(TAG+ "" + e.getMessage());
                e.printStackTrace();
            }

            return false;
        }
    }

    private boolean checkMagic() {
        if (rawMagic.toString().equals(magicString)) {
            return true;
        } else {
            return false;
        }
    }

    private boolean checkSN() {
//        String deviceSN = Build.SERIAL;
        String deviceSN = "";
        for (String SN : listSNs) {
            if (deviceSN.equals(SN)) {
                return true;
            }
        }

        if (dbg) {
            Log.error(TAG+ "license not for this device");
        }

        return false;
    }

    private boolean parseCompanyInfo() {
        int lineCount = 0;
        try (Scanner scan = new Scanner(rawCompanyInfo.toString())
        ) {
            while (scan.hasNextLine()) {
                lineCount += 1;
                String oneLine = scan.nextLine();
                String[] pair = oneLine.trim().split(":");
                if (pair.length != 2) {
                    if (dbg) {
                        Log.error(TAG+ "company info parse failed: " + lineCount);
                    }
                    return false;
                }
                mapCompanyInfo.put(propertyPrefix + pair[0].trim(), pair[1].trim());
            }

            if (dbg) {
                Log.error(TAG+ "=========company info====");
                for (HashMap.Entry<String, String> entry : mapCompanyInfo.entrySet()) {
                    String key = entry.getKey();
                    String value = entry.getValue();
                    Log.error(TAG+ key + ": " + value);
                }
                Log.error(TAG+ "=========================");
            }

            return true;
        } catch (Exception e) {
            if (dbg) {
                Log.error(TAG+ "" + e.getMessage());
                e.printStackTrace();
            }

            return false;
        }
    }

    private boolean parseSNs() {
        int lineCount = 0;
        try (Scanner scan = new Scanner(rawSNs.toString())
        ) {
            while (scan.hasNextLine()) {
                lineCount += 1;
                String oneLine = scan.nextLine();
                if (oneLine.trim().length() == 0) {
                    if (dbg) {
                        Log.error(TAG+ "SN parse failed: " + lineCount);
                    }
                    return false;
                }
                listSNs.add(oneLine.trim());
            }

            if (dbg) {
                Log.error(TAG+ "==========SN=============");
                for (String SN : listSNs) {
                    Log.error(TAG+ SN);
                }
                Log.error(TAG+ "=========================");
            }

            return true;
        } catch (Exception e) {
            if (dbg) {
                Log.error(TAG+ "" + e.getMessage());
                e.printStackTrace();
            }

            return false;
        }

    }

    private boolean parseSwitches() {
        int lineCount = 0;
        try (Scanner scan = new Scanner(rawSwitches.toString())
        ) {
            while (scan.hasNextLine()) {
                lineCount += 1;
                String oneLine = scan.nextLine();
                String[] lineSwitch = oneLine.trim().split("\\s+");
                if (lineSwitch.length != 5) {
                    if (dbg) {
                        Log.error(TAG+ "switch info parse failed: " + lineCount);
                    }
                    return false;
                }
                Switch tempSwitch = new Switch();
                tempSwitch.key = propertyPrefix + lineSwitch[0].trim();
                tempSwitch.type = lineSwitch[1].trim();
                tempSwitch.value = lineSwitch[2].trim();
                tempSwitch.defaultvalue = lineSwitch[3].trim();
                tempSwitch.expiretime = lineSwitch[4].trim();
                mapSwitches.put(lineSwitch[0].trim(), tempSwitch);
            }

            if (dbg) {
                Log.error(TAG+ "==========Switch=========");
                for (HashMap.Entry<String, Switch> entry : mapSwitches.entrySet()) {
                    String key = entry.getKey();
                    Switch value = entry.getValue();
                    Log.error(TAG+ key + " || " + value.toString());
                }
                Log.error(TAG+ "=========================");
            }

            return true;
        } catch (Exception e) {
            if (dbg) {
                Log.error(TAG+ "" + e.getMessage());
                e.printStackTrace();
            }

            return false;
        }
    }

    private boolean parseApps() {
        int lineCount = 0;
        try (Scanner scan = new Scanner(rawApps.toString())
        ) {
            while (scan.hasNextLine()) {
                lineCount += 1;
                String oneLine = scan.nextLine();
                if (oneLine.trim().length() == 0) {
                    if (dbg) {
                        Log.error(TAG+ "App section parse failed: " + lineCount);
                    }
                    return false;
                }

                String[] lineApp = oneLine.trim().split("\\s+");
                if (lineApp.length != 2) {
                    if (dbg) {
                        Log.error(TAG+ "App info parse failed: " + lineCount);
                    }
                    return false;
                }
                mapApps.put(lineApp[0].trim(), lineApp[1].trim());
            }

            if (dbg) {
                Log.error(TAG+ "==========App=============");
                for (String app : mapApps.keySet()) {
                    Log.error(TAG+ app + " expire at: " + mapApps.get(app));
                }
                Log.error(TAG+ "=========================");
            }

            return true;
        } catch (Exception e) {
            if (dbg) {
                Log.error(TAG+ "" + e.getMessage());
                e.printStackTrace();
            }

            return false;
        }

    }

    private boolean checkSignature() {
        boolean ret = Verifier.verifySignature(rawLicenseData.toString(),
                rawSignature.toString(),
                rawCertificate.toString()
        );
        if (ret) {
            if (dbg) {
                Log.error(TAG+ "signature verifing successfully");
            }

            return true;
        } else {
            if (dbg) {
                Log.error(TAG+ "signature verifing failed");
            }

            return false;
        }
    }

    //校验证书
    private boolean checkCertificate() {
        boolean ret = Verifier.verifyCertificate(rawCertificate.toString());
        if (ret) {
            if (dbg) {
                Log.error(TAG+ "certificate verifing successfully");
            }

            return true;
        } else {
            if (dbg) {
                Log.error(TAG+ "certificate verifing failed");
            }

            return false;
        }
    }

}
